/**
 * Calculates the IIR filter parameters and returns them as a byte array.
 *
 * @param frequency - The frequency at which the filter operates, in Hz.
 * @param gainDb - The gain of the filter in decibels.
 * @param qFactor - The quality factor of the filter.
 * @returns A Uint8Array containing the quantized filter coefficients.
 */
export function calculateIIRFilterParameters(
  frequency: number,
  gainDb: number,
  qFactor: number,
): Uint8Array {
  const SAMPLE_RATE = 96000
  const outputBytes = new Uint8Array(20)

  // 计算基本参数
  const linearGain = Math.sqrt(10 ** (gainDb / 20.0))
  const omega = (frequency * Math.PI * 2) / SAMPLE_RATE
  const sinOmega = Math.sin(omega) / (2.0 * qFactor)
  const cosOmega = Math.cos(omega)
  const normalizer = (sinOmega / linearGain) + 1.0

  // 计算分子和分母系数
  const numeratorCoeffs = [
    1.0,
    (-2.0 * cosOmega) / normalizer,
    (1.0 - (sinOmega / linearGain)) / normalizer,
  ]

  const denominatorCoeffs = [
    ((sinOmega * linearGain) + 1.0) / normalizer,
    (-2.0 * cosOmega) / normalizer,
    (1.0 - (sinOmega * linearGain)) / normalizer,
  ]

  // 量化系数
  const quantizedCoeffs = convertToFixedPoint(numeratorCoeffs, denominatorCoeffs)

  // 转换为字节数组
  for (let i = 0; i < quantizedCoeffs.length; i++) {
    const baseIndex = i * 4
    const value = quantizedCoeffs[i]
    outputBytes[baseIndex] = value & 0xFF
    outputBytes[baseIndex + 1] = (value >> 8) & 0xFF
    outputBytes[baseIndex + 2] = (value >> 16) & 0xFF
    outputBytes[baseIndex + 3] = (value >> 24) & 0xFF
  }

  return outputBytes
}

/**
 * Converts the given numerator and denominator coefficients to fixed-point representation.
 *
 * This function takes arrays of numerator and denominator coefficients, quantizes them
 * to a fixed-point representation using a scale factor, and returns an array of the
 * quantized coefficients. The fixed-point scale factor used is 2^30.
 *
 * @param numeratorCoeffs - An array of numerator coefficients to be converted.
 * @param denominatorCoeffs - An array of denominator coefficients to be converted.
 * @returns An array of quantized coefficients in fixed-point representation.
 */
function convertToFixedPoint(
  numeratorCoeffs: number[],
  denominatorCoeffs: number[],
): number[] {
  const FIXED_POINT_SCALE = 1 << 30

  const quantizeCoeff = (coeff: number): number =>
    Math.round(coeff * FIXED_POINT_SCALE)

  const quantizedDenominator = denominatorCoeffs.map(quantizeCoeff)
  const quantizedNumerator = numeratorCoeffs.map(quantizeCoeff)

  return [
    quantizedDenominator[0],
    quantizedDenominator[1],
    quantizedDenominator[2],
    -quantizedNumerator[1],
    -quantizedNumerator[2],
  ]
}
